﻿/*!
 * Copyright (c), 上海沃恩信息科技有限公司.
 * All rights reserved.
 * Licensed under BSD (https://www.pittypat.work/bsd.html).
 */

using System;
using System.Collections.Generic;
using System.Reflection;
using Pittypat.Configuration;

namespace Pittypat.Web
{
    /// <summary>
    /// 模块信息，同时提供对 Module 对象的缓存。使用缓存可以减少对 Api 类型的反射，以及重复创建 Api 对象实例。
    /// </summary>
    sealed class ModuleInfo
    {
        // 模块的路径，格式为：开发者代码.应用标识符.模块标识符
        private string path;
        // 模块 Api 的类型
        private Type type;
        // 模块 Api 类型的一个实例
        private Module module;
        // 隶属应用的配置信息
        private AppConfig appConfig;
        // 模块的配置信息
        private ModuleConfig config;
        // 模块上的元数据信息
        private ModuleAttribute attribute;
        // 模块是否正在维护
        private bool isMaintenance;
        // 模块自定义的维护信息
        private string maintenanceMessage;
        // 表示向客户端响应维护消息的功能信息
        private FunctionInfo maintenanceFunctionInfo;

        /// <summary>
        /// 使用模块的路径、模块类型、模块的对象实例、App 配置信息、模块的配置信息以及模块的属性初始化 <see cref="ModuleInfo"/> 类的新实例。
        /// </summary>
        /// <param name="path">模块的路径。</param>
        /// <param name="type">模块类型。</param>
        /// <param name="module">模块的对象实例。</param>
        /// <param name="appConfig">App 的配置信息。</param>
        /// <param name="config">模块的配置信息。</param>
        /// <param name="attribute">模块的属性。</param>
        private ModuleInfo(string path, Type type, Module module, AppConfig appConfig, ModuleConfig config, ModuleAttribute attribute)
        {
            this.path = path;
            this.type = type;
            this.module = module;
            this.appConfig = appConfig;
            this.config = config;
            this.attribute = attribute;
            this.isMaintenance = false;
            this.maintenanceMessage = "";
            this.maintenanceFunctionInfo = null;

            // 应用的维护状态覆盖模块的维护状态
            string status = (appConfig.Status ?? "").ToLower();
            if ("维护".Equals(status) || "maintenance".Equals(status))
            {
                this.isMaintenance = true;
                this.maintenanceMessage = "";
            }
            else if (status.StartsWith("维护:"))
            {
                this.isMaintenance = true;
                this.maintenanceMessage = status.Substring("维护:".Length);
            }
            else if (status.StartsWith("maintenance:"))
            {
                this.isMaintenance = true;
                this.maintenanceMessage = status.Substring("maintenance:".Length);
            }
            else
            {
                status = (config.Status ?? "").ToLower();
                if ("维护".Equals(status) || "maintenance".Equals(status))
                {
                    this.isMaintenance = true;
                    this.maintenanceMessage = "";
                }
                else if (status.StartsWith("维护:"))
                {
                    this.isMaintenance = true;
                    this.maintenanceMessage = status.Substring("维护:".Length);
                }
                else if (status.StartsWith("maintenance:"))
                {
                    this.isMaintenance = true;
                    this.maintenanceMessage = status.Substring("maintenance:".Length);
                }
            }

            if (this.isMaintenance)
            {
                this.maintenanceFunctionInfo = new FunctionInfo(null, this, null, null, null, null);
            }
        }

        /// <summary>
        /// 获取模块的路径。
        /// </summary>
        internal string Path
        {
            get
            {
                return this.path;
            }
        }

        /// <summary>
        /// 获取模块的类型。
        /// </summary>
        internal Type Type
        {
            get
            {
                return this.type;
            }
        }

        /// <summary>
        /// 获取模块对象实例。
        /// </summary>
        internal Module Module
        {
            get
            {
                return this.module;
            }
        }

        /// <summary>
        /// 获取 App 的配置信息。
        /// </summary>
        internal AppConfig AppConfig
        {
            get
            {
                return this.appConfig;
            }
        }

        /// <summary>
        /// 获取模块的配置信息。
        /// </summary>
        internal ModuleConfig Config
        {
            get
            {
                return this.config;
            }
        }

        /// <summary>
        /// 获取模块的属性。
        /// </summary>
        internal ModuleAttribute Attribute
        {
            get
            {
                return this.attribute;
            }
        }

        /// <summary>
        /// 获取一个值，该值指示当前模块是否正在维护当中。
        /// </summary>
        internal bool IsMaintenance
        {
            get
            {
                return this.isMaintenance;
            }
        }

        /// <summary>
        /// 获取模块的维护状态信息。
        /// </summary>
        internal string MaintenanceMessage
        {
            get
            {
                return this.maintenanceMessage;
            }
        }

        /// <summary>
        /// 获取一个特殊的 FunctionInfo，用于指示目标模块正在维护。
        /// </summary>
        internal FunctionInfo MaintenanceFunctionInfo
        {
            get
            {
                return this.maintenanceFunctionInfo;
            }
        }

        // 缓存字典，字典的键为模块的路径
        static readonly Dictionary<string, ModuleInfo> cachedModules = new Dictionary<string, ModuleInfo>(128, StringComparer.OrdinalIgnoreCase);
        // 对字典进行同步访问的对象
        static readonly object cachedModulesLock = new object();

        /// <summary>
        /// 查找指定路径的模块 Api 类型。
        /// </summary>
        /// <param name="developerCode">开发者代码。</param>
        /// <param name="appIdentifier">App 标识符。</param>
        /// <param name="moduleIdentifier">模块标识符。</param>
        /// <param name="path">模块的完整路径。</param>
        /// <returns>从 Module 继承而来的一个类型。如果没有找到，则返回 null。</returns>
        static Type FindApiType(string developerCode, string appIdentifier, string moduleIdentifier, string path)
        {
            var assembly = AssemblyHelper.FindAssembly(string.Format("{0}.{1}.Api", developerCode, appIdentifier));
            if (assembly != null)
            {
                var type = assembly.GetType(string.Format("{0}.Api", path), false, true);

                if (type != null)
                {
                    if (type.IsClass && !type.IsAbstract && !type.IsGenericType && type.IsSubclassOf(typeof(Module)))
                    {
                        return type;
                    }
                }
            }

            return null;
        }

        /// <summary>
        /// 根据模块的路径获取模块的信息。如果此前已经缓存，则返回缓存中的信息，否则创建新的信息，并缓存。
        /// </summary>
        /// <param name="developerCode">开发者代码。</param>
        /// <param name="appIdentifier">App标识符。</param>
        /// <param name="moduleIdentifier">模块标识符。</param>
        /// <returns>如果没有找到模块信息，则返回 null。</returns>
        internal static ModuleInfo Get(string developerCode, string appIdentifier, string moduleIdentifier)
        {
            // 模块的路径
            string path = string.Format("{0}.{1}.{2}", developerCode, appIdentifier, moduleIdentifier);
            ModuleInfo info = null;

            lock (cachedModulesLock)
            {
                if (!cachedModules.TryGetValue(path, out info))
                {
                    // 获取配置信息
                    AppConfig appConfig = PittypatConfig.Current.Apps[developerCode, appIdentifier];
                    if (appConfig == null)
                    {
                        // 没有找到 App 配置
                        return null;
                    }

                    ModuleConfig config = appConfig.Modules[moduleIdentifier];
                    if (config == null)
                    {
                        return null;
                    }

                    // 查找模块的类型
                    var type = FindApiType(developerCode, appIdentifier, moduleIdentifier, path);

                    if (type == null)
                    {
                        // 没有找到目标模块的类型
                        return null;
                    }

                    // 模块属性信息
                    var attribute = type.GetCustomAttribute<ModuleAttribute>() ?? new ModuleAttribute(ModulePresentation.UI)
                    {
                        AllowAnonymous = false
                    };

                    // 模块对象实例
                    Module module = null;

                    if (attribute.Presentation != ModulePresentation.Internal)
                    {
                        // 只有不是内部模块时才会创建模块对象
                        module = (Module)Activator.CreateInstance(type);
                    }

                    info = new ModuleInfo(path, type, module, appConfig, config, attribute);
                    // 添加到缓存中
                    cachedModules.Add(path, info);
                }
            }

            return info;
        }
    }
}
